package com.main.common.aspects;

import com.google.common.collect.ImmutableList;
import com.main.common.base.resp.RespBody;
import com.main.common.base.resp.RespCode;
import com.main.common.enums.LimitType;
import com.main.common.utils.exceptions.LimitAccessException;
import com.main.common.interfaces.AccessLimit;
import com.main.common.utils.IPUtils;
import com.main.common.utils.exceptions.StackTraceLog;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@Aspect
@Component
@Order(3)
public class AccessLimitAspect {

    @Autowired
    private RedisTemplate<String, Object> limitRedisTemplate;
    //DefaultRedisScript 是 Spring Data Redis 框架提供的一个类，用于简化在 Spring 应用程序中使用 Redis 脚本（如Lua脚本）的过程
    private static final DefaultRedisScript<Long>  DEFAULT_SCRIPT;
    //脚本静态代码块初始化
    static {
        DEFAULT_SCRIPT = new DefaultRedisScript<>();
        //在resources目录下导入脚本文件
        DEFAULT_SCRIPT.setLocation(new ClassPathResource("access_limit.lua"));
        DEFAULT_SCRIPT.setResultType(Long.class);
    }
    @Pointcut("@annotation(com.main.common.interfaces.AccessLimit)")
    public void pointcut() {
        // do nothing
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("-------------------------------------AccessLimitAspect-----------------------------------");
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        AccessLimit limitAnnotation = method.getAnnotation(AccessLimit.class);
        LimitType limitType = limitAnnotation.limitType();
        String name = limitAnnotation.name();
        String key;
        int limitPeriod = limitAnnotation.period();
        int limitCount = limitAnnotation.count();
        key = switch (limitType) {
            case IP -> IPUtils.getIpAddr(request);
            case CUSTOMER -> limitAnnotation.key();
            default -> StringUtils.upperCase(method.getName());
        };
        ImmutableList<String> keys = ImmutableList.of(StringUtils.join(limitAnnotation.prefix() + "_", key));
        System.out.println(keys);
        Number count = limitRedisTemplate.execute(DEFAULT_SCRIPT, keys, limitCount, limitPeriod);
        log.info("第{}次访问key为 {}，描述为 [{}] 的接口", count, keys, name);
        if (count != null && count.intValue() <= limitCount) {
            return point.proceed();
        } else {
            StackTraceLog stackTraceLog = new StackTraceLog();
            Exception e= new LimitAccessException("接口访问超出频率限制");
            stackTraceLog.saveStackTraceLog(e);
            return RespBody.fail(RespCode.TOO_MANY_REQUESTS,"接口访问超出频率限制","");
        }
    }
}